#ifndef GRAPHICS_H
#define GRAPHICS_H

#include "logger.h"

#define LOG_TAG "Asteroids"

class AFProgram
{
private:
	/**
	 * Load shader helper function
	 * @param shaderSrc string of shader source
	 * @param type shader type
	 * @return created shader
	 */
	GLuint loadShader(const GLchar* shaderSrc, GLenum type)
	{
		GLuint shader = glCreateShader(type);
		if (shader == 0)
			return 0;

		glShaderSource(shader, 1, &shaderSrc, NULL);
		glCompileShader(shader);

		GLint compiled;
		glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);

		if (!compiled)
		{
			GLint infoLen = 0;
			glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);

			if (infoLen > 0)
			{
				GLchar* infoLog = new GLchar[infoLen];
				glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
				LOG_INFO("Error compiling shader: %s", infoLog);
				delete[] infoLog;
			}

			glDeleteShader(shader);
			return 0;
		}

		return shader;
	}

private:
	GLuint _vertexShader;
	GLuint _fragmentShader;
	GLuint _programObject;

public:
	AFProgram() : _vertexShader(0), _fragmentShader(0), _programObject(0)
	{}

	AFProgram(const GLchar* vertexShaderSrc, const GLchar* fragmentShaderSrc) : _vertexShader(0), _fragmentShader(0), _programObject(0)
	{
		initProgram(vertexShaderSrc, fragmentShaderSrc);
	}

	/**
	 * Create program, build and attach the vertex and fragment shaders from text sources
	 * @param vertexShaderSrc vertex shader source
	 * @param fragmentShaderSrc fragment shader source
	 */
	bool initProgram(const GLchar* vertexShaderSrc, const GLchar* fragmentShaderSrc)
	{
		_vertexShader = loadShader(vertexShaderSrc, GL_VERTEX_SHADER);

		if (_vertexShader)
		{
			_fragmentShader = loadShader(fragmentShaderSrc, GL_FRAGMENT_SHADER);

			if (_fragmentShader)
			{
				_programObject = glCreateProgram();

				if (_programObject == 0)
				{
					glDeleteShader(_vertexShader);
					glDeleteShader(_fragmentShader);

					_vertexShader = 0;
					_fragmentShader = 0;

					return false;
				}
				else
				{
					glAttachShader(_programObject, _vertexShader);
					glAttachShader(_programObject, _fragmentShader);
				}
			}
			else
			{
				glDeleteShader(_vertexShader);
				_vertexShader = 0;

				return false;
			}
		}

		return true;
	}

	virtual ~AFProgram()
	{
		if (_programObject == 0)
			return;

		if (_vertexShader)
		{
			glDetachShader(_programObject, _vertexShader);
			glDeleteShader(_vertexShader);
			_vertexShader = 0;
		}

		if (_fragmentShader)
		{
			glDetachShader(_programObject, _fragmentShader);
			glDeleteShader(_fragmentShader);
			_fragmentShader = 0;
		}

		glDeleteProgram(_programObject);
		_programObject = 0;
	}

	/**
	 * GLuint type cast for using AFProgram like glProgram
	 */
	operator GLuint() {return _programObject;}

	/**
	 * Indicate that program is ready to use
	 */
	bool isInitialized() {return _programObject != 0;}
};

#endif
